package com.insight.service;

import com.insight.dto.datasource.ConnectionTestRequest;
import com.insight.dto.datasource.ConnectionTestResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;

/**
 * T037: 连接测试服务层
 * 实现数据库连接测试逻辑，包括超时控制和错误处理
 *
 * @author Insight Team
 */
@Slf4j
@Service
public class ConnectionTestService {

    /**
     * 测试数据库连接
     * 验证连接参数是否正确，获取数据库元信息
     *
     * @param request 连接测试请求
     * @return 连接测试响应（包含成功/失败信息、响应时间、数据库版本等）
     */
    public ConnectionTestResponse testConnection(ConnectionTestRequest request) {
        log.info("开始测试数据库连接: dbType={}, host={}, port={}, database={}",
                request.getDbType(), request.getHost(), request.getPort(), request.getDatabaseName());

        long startTime = System.currentTimeMillis();
        Connection connection = null;

        try {
            // 1. 构建JDBC URL
            String jdbcUrl = request.getDbType().buildJdbcUrl(
                    request.getHost(),
                    request.getPort(),
                    request.getDatabaseName()
            );
            log.debug("JDBC URL: {}", jdbcUrl);

            // 2. 设置连接属性
            Properties props = new Properties();
            props.setProperty("user", request.getUsername());
            props.setProperty("password", request.getPassword());

            // 3. 设置连接超时（毫秒转秒）
            int timeoutSeconds = request.getTimeoutMs() / 1000;
            DriverManager.setLoginTimeout(timeoutSeconds);

            // 4. 建立连接
            connection = DriverManager.getConnection(jdbcUrl, props);

            // 5. 验证连接有效性
            if (!connection.isValid(timeoutSeconds)) {
                log.warn("连接验证失败: 数据库连接无效");
                return ConnectionTestResponse.failure("连接验证失败", "数据库连接无效");
            }

            // 6. 获取数据库元信息
            DatabaseMetaData metaData = connection.getMetaData();
            if (metaData == null) {
                log.warn("无法获取数据库元信息");
                return ConnectionTestResponse.failure("元信息获取失败", "无法获取数据库元信息");
            }

            String databaseProductName = metaData.getDatabaseProductName();
            String databaseVersion = metaData.getDatabaseProductVersion();
            String driverVersion = metaData.getDriverVersion();

            // 7. 计算响应时间
            long responseTimeMs = System.currentTimeMillis() - startTime;

            log.info("数据库连接测试成功: product={}, version={}, responseTime={}ms",
                    databaseProductName, databaseVersion, responseTimeMs);

            // 8. 返回成功响应
            return ConnectionTestResponse.success(
                    responseTimeMs,
                    databaseVersion,
                    databaseProductName,
                    driverVersion
            );

        } catch (SQLException e) {
            // SQL异常处理
            long responseTimeMs = System.currentTimeMillis() - startTime;
            log.error("数据库连接测试失败: SQLState={}, ErrorCode={}, Message={}",
                    e.getSQLState(), e.getErrorCode(), e.getMessage(), e);

            String errorMessage = parseErrorMessage(e);
            String errorDetails = String.format("SQLState: %s, ErrorCode: %d, Message: %s",
                    e.getSQLState(), e.getErrorCode(), e.getMessage());

            return ConnectionTestResponse.failure(errorMessage, errorDetails);

        } catch (Exception e) {
            // 其他异常处理
            log.error("数据库连接测试失败: {}", e.getMessage(), e);

            String errorMessage = "连接失败: " + e.getClass().getSimpleName();
            String errorDetails = e.getMessage() != null ? e.getMessage() : "未知错误";

            return ConnectionTestResponse.failure(errorMessage, errorDetails);

        } finally {
            // 9. 关闭连接
            closeConnection(connection);
        }
    }

    /**
     * 安全关闭数据库连接
     *
     * @param connection 数据库连接
     */
    private void closeConnection(Connection connection) {
        if (connection != null) {
            try {
                connection.close();
                log.debug("数据库连接已关闭");
            } catch (SQLException e) {
                log.warn("关闭数据库连接时发生异常: {}", e.getMessage());
            }
        }
    }

    /**
     * 解析SQL异常，返回用户友好的错误消息
     *
     * @param e SQL异常
     * @return 用户友好的错误消息
     */
    private String parseErrorMessage(SQLException e) {
        String sqlState = e.getSQLState();
        int errorCode = e.getErrorCode();
        String message = e.getMessage();

        // 根据SQLState和错误代码判断错误类型
        if (sqlState != null) {
            // 认证失败 (28000)
            if (sqlState.startsWith("28")) {
                return "认证失败: 用户名或密码错误";
            }
            // 连接失败 (08xxx)
            if (sqlState.startsWith("08")) {
                if (message.contains("timeout") || message.contains("超时")) {
                    return "连接超时: 无法在指定时间内连接到数据库";
                }
                if (message.contains("refused") || message.contains("拒绝")) {
                    return "连接被拒绝: 请检查主机地址和端口号";
                }
                if (message.contains("host") || message.contains("主机")) {
                    return "主机不可达: 无法找到指定的数据库主机";
                }
                return "连接失败: 无法连接到数据库服务器";
            }
            // 数据库不存在 (3D000 for PostgreSQL, 42000 for MySQL)
            if (sqlState.equals("3D000") || sqlState.equals("42000")) {
                if (message.contains("database") || message.contains("数据库")) {
                    return "数据库不存在: 指定的数据库名称不正确";
                }
            }
        }

        // MySQL特定错误代码
        if (errorCode == 1045) {
            return "认证失败: 用户名或密码错误";
        }
        if (errorCode == 1049) {
            return "数据库不存在: 指定的数据库名称不正确";
        }
        if (errorCode == 2003 || errorCode == 2002) {
            return "连接失败: 无法连接到数据库服务器";
        }

        // Oracle特定错误代码
        if (errorCode == 1017) {
            return "认证失败: 用户名或密码错误";
        }
        if (errorCode == 12154 || errorCode == 17002) {
            return "连接失败: 无法连接到数据库服务器";
        }

        // SQL Server特定错误代码
        if (errorCode == 18456) {
            return "认证失败: 用户名或密码错误";
        }
        if (errorCode == 4060) {
            return "数据库不存在: 指定的数据库名称不正确";
        }

        // PostgreSQL特定错误
        if (message.contains("password authentication failed")) {
            return "认证失败: 用户名或密码错误";
        }
        if (message.contains("does not exist")) {
            return "数据库不存在: 指定的数据库名称不正确";
        }

        // 通用错误
        if (message.contains("timeout") || message.contains("超时")) {
            return "连接超时: 无法在指定时间内连接到数据库";
        }
        if (message.contains("refused") || message.contains("拒绝")) {
            return "连接被拒绝: 请检查主机地址和端口号";
        }

        // 默认错误消息
        return "连接失败: " + (message != null ? message : "未知错误");
    }

    /**
     * 测试数据源ID对应的连接
     * 用于验证已保存的数据源配置
     *
     * @param dataSourceId  数据源ID
     * @param encryptionKey 密码解密密钥
     * @return 连接测试响应
     */
    public ConnectionTestResponse testDataSourceById(Long dataSourceId, String encryptionKey) {
        log.info("测试数据源连接: dataSourceId={}", dataSourceId);

        // TODO: 在T038-T040实现Controller层时，通过DataSourceService获取数据源配置
        // 并构建ConnectionTestRequest进行测试

        throw new UnsupportedOperationException("此方法将在Controller层实现时完成");
    }
}
